﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PasswordEntry.cs" company="Mar3ek">
//   This code is licensed under the Mozilla Public License 2.0.
//   You can find the full license text at the following address:
//   https://winkee.codeplex.com/license
// </copyright>
// <summary>
//   The password entry.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace WinKee.Models
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.Text.RegularExpressions;

    using KeePassLib;
    using KeePassLib.Cryptography;

    /// <summary>
    /// The password entry.
    /// </summary>
    [DataContract]
    public class PasswordEntry
    {
        private uint? entropy;

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="PasswordEntry"/> class.
        /// </summary>
        /// <param name="entry">
        /// The entry.
        /// </param>
        public PasswordEntry(PwEntry entry)
        {
            
            if (entry.Strings.Exists("Title"))
            {
                this.Title = entry.Strings.Get("Title").ReadString();
            }

            if (string.IsNullOrEmpty(this.Title))
            {
                this.Title = "(no title)";
                this.GroupingKey = ' ';
            }
            else
            {
                this.GroupingKey = char.ToUpperInvariant(this.Title[0]);
            }

            this.UserName = entry.Strings.Exists("UserName") ? entry.Strings.Get("UserName").ReadString() : string.Empty;
            this.Password = entry.Strings.Exists("Password") ? entry.Strings.Get("Password").ReadString() : string.Empty;
            this.HasExpiration = entry.Expires;
            this.ExpirationTime = entry.ExpiryTime;
            this.ModificationTime = entry.LastModificationTime;

            if (Regex.IsMatch(entry.ForegroundColor, "^#?[0-9a-f]{2}[0-9a-f]{2}[0-9a-f]{2}$", RegexOptions.IgnoreCase))
            {
                this.ForegroundColor = entry.ForegroundColor;
            }

            var pathComponents = new List<string>();

            var parent = entry.ParentGroup;

            while (parent != null)
            {
                pathComponents.Add(parent.Name);
                parent = parent.ParentGroup;
            }

            this.Path = string.Join("\\", Enumerable.Reverse(pathComponents));

            try
            {
                this.Url = entry.Strings.Exists("URL") ? new Uri(entry.Strings.Get("URL").ReadString()) : null;
            }
            catch (Exception)
            {
                this.Url = null;
            }
        }

        [DataMember]
        public string ForegroundColor { get; set; }

        [DataMember]
        public char GroupingKey { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="PasswordEntry"/> class.
        /// </summary>
        /// <remarks>
        /// For serialization.
        /// </remarks>
        public PasswordEntry()
        {
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the password.
        /// </summary>
        [DataMember]
        public string Password { get; set; }

        /// <summary>
        /// Gets or sets the title.
        /// </summary>
        [DataMember]
        public string Title { get; set; }

        /// <summary>
        /// Gets or sets the user name.
        /// </summary>
        [DataMember]
        public string UserName { get; set; }

        /// <summary>
        /// Gets or sets the modification time.
        /// </summary>
        [DataMember]
        public DateTime ModificationTime { get; set; }

        /// <summary>
        /// Gets or sets the expiration time.
        /// </summary>
        [DataMember]
        public DateTime ExpirationTime { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether this entry expires.
        /// </summary>
        [DataMember]
        public bool HasExpiration { get; set; }

        /// <summary>
        /// Gets or sets the url.
        /// </summary>
        [DataMember]
        public Uri Url { get; set; }

        [IgnoreDataMember]
        public uint PasswordEntropy
        {
            get
            {
                if (this.entropy == null)
                {
                    this.entropy = QualityEstimation.EstimatePasswordBits(this.Password.ToCharArray());
                }

                return this.entropy.Value;
            }
        }

        [IgnoreDataMember]
        public string PasswordStrength
        {
            get
            {
                if (this.entropy == null)
                {
                    this.entropy = QualityEstimation.EstimatePasswordBits(this.Password.ToCharArray());
                }

                if (this.entropy <= 64)
                {
                    return "Very weak";
                }

                if (this.entropy > 65 && this.entropy <= 80)
                {
                    return "Weak";
                }
                
                if (this.entropy > 80 && this.entropy <= 112)
                {
                    return "Moderate";
                }
                
                if (this.entropy > 112 && this.entropy <= 128)
                {
                    return "Strong";
                }
                
                return this.entropy > 128 ? "Very strong" : string.Empty;
            }
        }

        [IgnoreDataMember]
        public double PasswordEntropyPercent
        {
            get
            {
                if (this.entropy == null)
                {
                    this.entropy = QualityEstimation.EstimatePasswordBits(this.Password.ToCharArray());
                }

                return Math.Min(this.entropy.Value / 128.0, 1);
            }
        }

        /// <summary>
        /// Gets or sets the path.
        /// </summary>
        [DataMember]
        public string Path { get; set; }


        #endregion
    }
}